package com.blue.http.config;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
 * 内存实现认证配置
 * 
 * @author zhengj
 * @since 1.0 2017年3月1日
 */
public abstract class MemAuthConfig implements AuthConfig
{
	private static Logger logger = LoggerFactory.getLogger(MemAuthConfig.class);
	
	private Map<String, String> secretCache = new HashMap<>(); // <name, secret>
	private Map<String, Set<String>> roleUrlCache = new HashMap<>(); // <roleId, Set<URL>>
	private Map<String, List<String>> accountRoleCache = new HashMap<>(); // <name, List<roleId>>
	private Map<String, String> tokenAccountCache = new HashMap<>(); // <token, name>
	private Map<String, Long> tokenExpireCache = new HashMap<>(); // <token, time>
	
	
	public MemAuthConfig()
	{
	}

	@Override
	public final String login(String name, String secret)
	{
		Assert.hasText(name, "用户名不能为空");
		Assert.hasText(secret, "密钥不能为空");
		String s = secretCache.get(name);
		if (s == null || s.isEmpty())
		{
			synchronized (this)
			{
				if (s == null || s.isEmpty())
				{
					s = this.getAccount(name);
				}
				if (s != null && !s.isEmpty())
				{
					secretCache.put(name, s);
				}
			}
		}
		
		if (secret.equals(s)) // 登录成功
		{
			String token = UUID.randomUUID().toString();
			tokenAccountCache.put(token, name);
			tokenExpireCache.put(token, System.currentTimeMillis());
			logger.info("登录成功：name={}, token={}", name, token);
			return token;
		}
		
		return null;
	}
	
	@Override
	public final boolean verify(String token)
	{
		Assert.hasText(token, "令牌不能为空");
		Long expire = tokenExpireCache.get(token);
		if (expire == null || System.currentTimeMillis() - expire.longValue() > AuthConfig.expire * 1000)
		{
			logger.warn("令牌无效：token={}", token);
			tokenExpireCache.remove(token);
			tokenAccountCache.remove(token);
			return false;
		}
		
		return true;
	}

	@Override
	public final boolean auth(String token, String url)
	{
		Set<String> set = this.noAuth();
		if (set != null && set.contains(url))
			return true;
		
		Assert.hasText(token, "令牌不能为空");
		Assert.hasText(url, "链接不能为空");
		
		if (!this.verify(token))
			return false;
		
		String name = tokenAccountCache.get(token);
		if (name == null || name.isEmpty())
			return false;
		
		List<String> roleList = accountRoleCache.get(name);
		if (roleList == null || roleList.isEmpty())
		{
			synchronized (this)
			{
				if (roleList == null || roleList.isEmpty())
				{
					roleList = this.listAccountRole(name);
				}
				if (roleList != null && !roleList.isEmpty())
				{
					accountRoleCache.put(name, roleList);
				}
			}
			if (roleList == null || roleList.isEmpty())
				return false;
			
		}
		
		for (String role : roleList)
		{
			Set<String> urlSet = roleUrlCache.get(role);
			if (urlSet == null || urlSet.isEmpty())
			{
				List<String> urlList = null;
				synchronized (this)
				{
					if (urlSet == null || urlSet.isEmpty())
					{
						urlList = this.listRoleUrl(role);
					}
					if (urlList != null && !urlList.isEmpty())
					{
						urlSet = new HashSet<>();
						urlSet.addAll(urlList);
						roleUrlCache.put(role, urlSet);
					}
				}
				if (urlList == null || urlList.isEmpty())
					continue;
				
			}
			
			if (urlSet.contains(url))
			{
				logger.info("有权限访问：{}", url);
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 清空帐号名称和密钥
	 */
	@Override
	public final void clearSecret()
	{
		secretCache.clear();
	}
	
	/**
	 * 清空角色－URL关联关系
	 */
	@Override
	public final void clearAuth()
	{
		accountRoleCache.clear();
		roleUrlCache.clear();
	}
	
	/**
	 * 返回不需要认证的路径
	 * 
	 * @return
	 */
	protected Set<String> noAuth()
	{
		Set<String> set = new HashSet<>();
		set.add("/token");
		return set;
	}
	
	/**
	 * 通过帐号名称获取帐号密钥
	 * 
	 * @param name 帐号名称
	 * @return 帐号密钥，不存在返回 null
	 */
	protected abstract String getAccount(String name);
	
	/**
	 * 通过帐号名称获取角色列表
	 * 
	 * @param name 帐号名称
	 * @return 角色列表，不存在返回 null
	 */
	protected abstract List<String> listAccountRole(String name);
	
	/**
	 * 通过角色获取URL列表
	 * 
	 * @param role 角色
	 * @return URL列表，不存在返回 null
	 */
	protected abstract List<String> listRoleUrl(String role);
	
}
